Skip to content

ROS2 服务通信 ​

服务概念 ​

1. 什么是服务 ​

在 ROS 2 中,**服务(Service)**是一种同步式请求—响应通信机制。它适合处理"调用一次,返回一次结果"的任务。

例如:

  • 请求当前两个数的和;
  • 请求机器人执行一次固定操作;
  • 请求返回当前某项状态信息。

与话题通信不同,服务通信不是持续广播,而是:

  • 客户端发起请求;
  • 服务端接收请求并处理;
  • 服务端返回结果;
  • 客户端获得响应。
text
客户端发送请求 → 服务端处理 → 返回响应

2. 服务与话题的区别 ​

话题(Topic) ​

  • 适合连续发送数据
  • 是异步通信
  • 一般用于传感器数据、状态流等持续信息交换

服务(Service) ​

  • 适合一次请求、一次返回
  • 更像函数调用
  • 一般用于命令触发、状态查询、参数计算等任务

3. 服务端与客户端的关系 ​

在 ROS 2 服务通信中:

  • 服务端(Service Server):负责接收请求并返回响应;
  • 客户端(Client):负责发送请求并等待结果。

说明示例

  • 客户端发送两个整数 a=2, b=3;
  • 服务端执行加法运算;
  • 服务端返回结果 sum=5;
  • 客户端打印结果。

4. 服务通信的基本构成 ​

一个 ROS 2 Python 服务系统通常包括以下内容:

  1. 导入 rclpy 与 Node;
  2. 导入服务接口类型,例如 example_interfaces.srv.AddTwoInts;
  3. 在服务端节点中调用 create_service();
  4. 在客户端节点中调用 create_client();
  5. 服务端编写请求处理回调函数;
  6. 客户端构造请求并发送;
  7. 运行节点并验证服务响应。

学习内容六:服务通信实践 ​

(一)创建实验功能包 ​

bash
ros2 pkg create --build-type ament_python learn_service --dependencies rclpy example_interfaces

这里使用 example_interfaces 中的 AddTwoInts 服务接口。

这个服务接口的客户端的数据是两个 int64 的数,分别为 a 和 b,服务端的数据是 int64 的 sum,结构如下:

srv
int64 a
int64 b
---
int64 sum

(二)编写客户端节点 ​

在功能包下新建文件 service_client.py:

python
import rclpy                                  # ROS2 Python接口库
from rclpy.node   import Node                 # ROS2 节点类
from example_interfaces.srv import AddTwoInts                # 自定义的服务接口

class AddClient(Node):
    def __init__(self, node_name, service_name):
        super().__init__(node_name)
        self.client_ = self.create_client(AddTwoInts,service_name) # 创建服务客户端对象(服务接口类型,服务名)
        while not self.client_.wait_for_service(timeout_sec=1.0):# 循环等待服务器端成功启动
            self.get_logger().warn('service not available, waiting again...')

        self.request_ = AddTwoInts.Request() # 创建服务请求的数据对象

    def send_request(self, a, b):   # 创建一个发送服务请求的函数
        self.request_.a = a
        self.request_.b = b
        self.future = self.client_.call_async(self.request_) # 发送请求数据
        return self.future

def main(args = None):
    rclpy.init(args=args)
    client_node = AddClient('service_adder_client',service_name='add_two_ints') # 创建ROS2节点对象并进行初始化

    a = 10
    b = 20
    future = client_node.send_request(a, b)  # 发送服务请求,比如说求解10+20

    rclpy.spin_until_future_complete(client_node,future) # 阻塞当前线程,直到某个 Future 对象完成(即收到服务响应、动作结果等)。

    if future.result() is not None:
        response = future.result() # future.result() 获取服务端返回的结果
        client_node.get_logger().info(f'第一个加数为:{a},第二个加数为{b},加和的结果为: {response.sum}')  # response.sum是返回的数据的变量名是sum
    else:
        client_node.get_logger().error('服务请求失败')

    client_node.destroy_node()
    rclpy.shutdown()

(三)编写服务端节点 ​

在功能包下新建文件 service_server.py:

python
import rclpy                                     # ROS2 Python接口库
from rclpy.node   import Node                    # ROS2 节点类
from example_interfaces.srv import AddTwoInts    # 自定义的服务接口

class adderServer(Node):
    def __init__(self, node_name, service_name):
        super().__init__(node_name)
        self.server_ = self.create_service(AddTwoInts,service_name, self.adder_callback) # 创建服务器对象(接口类型、服务名、服务器回调函数)

    def adder_callback(self, request:AddTwoInts.Request, response:AddTwoInts.Response): # 创建回调函数,执行收到请求后对数据的处理
        response.sum = request.a + request.b # 完成加法求和计算,将结果放到反馈的数据中
        self.get_logger().info(f'收到请求,计算{request.a} + {request.b}')
        return response # 反馈应答信息

def main(args=None):                             # ROS2节点主入口main函数
    rclpy.init(args=args)                        # ROS2 Python接口初始化
    server_node = adderServer("service_adder_server",service_name='add_two_ints')   # 创建ROS2节点对象并进行初始化
    rclpy.spin(server_node)                             # 循环等待ROS2退出
    server_node.destroy_node()                          # 销毁节点对象
    rclpy.shutdown()                             # 关闭ROS2 Python接口

(四)修改 setup.py ​

python
entry_points={
    'console_scripts': [
        'service_adder_client  = learn_service.service_client:main',
        'service_adder_server  = learn_service.service_server:main',
    ],
},

(五)编译功能包 ​

返回工作空间根目录:

bash
colcon build
source install/setup.bash

(六)运行服务端与客户端节点 ​

1. 运行服务端节点 ​

打开第一个终端,到工作空间根目录:

bash
source install/setup.bash
ros2 run learn_service service_adder_server

服务端启动后会一直运行,等待客户端请求。

2. 运行客户端节点 ​

打开第二个终端,到工作空间根目录:

bash
source install/setup.bash
ros2 run learn_service service_adder_client

这表明客户端成功发送了请求,服务端成功进行了处理,并返回了结果。可以看出服务端一直在运行。

使用命令行工具验证服务通信 ​

1. 查看当前服务列表 ​

bash
ros2 service list

2. 查看服务类型 ​

bash
ros2 service type /add_two_ints

3. 直接通过命令行调用服务 ​

本质是通过命令行模拟了一个客户端:

bash
# 格式 ros2 service call 服务名 数据接口 "{字典形式表示的请求数据}"
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 4, b: 5}"

服务通信成功的条件 ​

要实现服务通信,至少需要满足以下条件:

说明必须满足的条件

  • 客户端和服务端的服务名一致;
  • 客户端和服务端的服务类型一致;
  • 服务端已经处于运行状态;
  • 客户端能够成功连接服务。